<?php

/**
 * @todo Cache for Table Info
 * @todo Wake up feature
 * @todo Problems with bindings in select function
 */

// CONFIG
if (!defined('ADEPT_CACHE_METADATA')) {
    define('ADEPT_CACHE_METADATA', false);
}

class Adept_Db_Table
{

    const CACHE_GROUP = 'db_table_metadata';

    private $cacheMetadata = ADEPT_CACHE_METADATA;

    protected $connection;
    protected $tableName;
    protected $tableInfo;


    /**
     * @param string $tableName
     * @param mixed $connection
     * @throws Adept_Db_Exception
     */
    public function __construct($tableName = '', $connection = null) 
    {
        if (is_null($connection) || is_string($connection)) {
            $this->connection = Adept_Db_Factory::getConnection($connection);
        } elseif ($connection instanceof Adept_Db_IConnection) {
            $this->connection = $connection;
        } else {
            throw new Adept_Db_Exception('Invalid type of connection parameter ');
        }
        if ($tableName) {
            $this->setTableName($tableName);
        }
    }

    protected function retriveTableInfo() 
    {

        /*    if ($this->cacheMetadata) {
        $cacheService = new CacheService(new FileCacheStorage());
        $metadata = $cacheService->getValue($this->tableName, self::CACHE_GROUP);
        } else {
        $metadata = null;
        }

        if ($metadata == null) {
        $metadata = $this->connection->getTableMetadata($this->tableName);
        if ($this->cacheMetadata) {
        $cacheService->putValue($metadata, $this->tableName, self::CACHE_GROUP);
        }
        }
        */
        $tableInfo = $this->connection->getTableInfo($this->tableName);
        return $tableInfo;
    }

    public function getTableInfo()
    {
        return $this->tableInfo;
    }

    public function getPrimaryKey()
    {
        return $this->getTableInfo()->getPrimaryKey();
    }

    protected function filterRecord($row) 
    {
        $filtered = array();
        foreach ($row as $name => $value) {
            if ($this->getTableInfo()->hasColumn($name)) {
                $filtered[$name] = $value;
            }
        }
        return $filtered;
    }

    /**
     * Delete rows from database table by condition
     *
     * @param string $where Condition string
     * @return int Number of deleted rows
     */
    public function delete($where) 
    {
        if (empty($where)) {
            return ;
        }
        $delete = $this->connection->getDeleteQuery();
        $delete->table($this->tableName)
        ->where($where);
        $query = $this->connection->newStatement($delete);
        $query->execute();
        return $query->getAffectedRowCount();
    }

    public function deleteAll() 
    {
        $delete = $this->connection->getDeleteQuery();
        $delete->table($this->tableName);
        $query = $this->connection->newStatement($delete);
        $query->execute();
        return $query->getAffectedRowCount();
    }

    public function deleteById($id) 
    {
        $where= $this->getPrimaryKey() . " = '" . $id . "'";
        return $this->delete($where);
    }

    public function select($where = null, $order = null, $count = null, $offset = null) 
    {

        $query = $this->connection->getSelectQuery();

        $query->from("`{$this->tableName}`");
        if (!is_null($where)) {
            $query->where($where);
        }

        if (!is_null($order)) {
            $query->order($order);
        }

        if (!is_null($offset) || !is_null($count)) {
            $query->limit($count, $offset);
        }

        $statement = $this->connection->newStatement($query);
        return $statement->getRecordSet();
    }

    public function selectById($id)
    {
        $where = $this->getPrimaryKey() . " = '" . $id . "'";
        return $this->selectFirst($where);
    }

    public function selectFirst($where = null, $order = null, $offset = null) 
    {
        $recordSet = $this->select($where, $order, 1, $offset);
        if ($recordSet) {
            return $recordSet->getFirstRecord();
        } else {
            return null;
        }
    }

    public function insert($values) 
    {
        $values = $this->filterRecord($values);

        $insert = $this->connection->getInsertQuery();
        $insert->into($this->tableName)
               ->binds(array_keys($values));

        $query = $this->connection->newStatement($insert);

        foreach ($values as $key => $value) {
            $query->setAs($key, $value, $this->getTableInfo()->getColumnInfo($key)->getMetaType());
        }
        $query->execute();
        return $query->getInsertId();
    }

    public function update($values, $where = '') 
    {
        $values = $this->filterRecord($values);

        $update = $this->connection->getUpdateQuery();
        $update->table($this->tableName)
               ->binds(array_keys($values));

        if (!empty($where)) {
            $update->where($where);
        }

        $query = $this->connection->newStatement($update);

        foreach ($values as $key => $value) {
            $query->setAs($key, $value, $this->getTableInfo()->getColumnInfo($key)->getMetaType());
        }
        $query->execute();
        return $query->getAffectedRowCount();
    }

    public function updateById($values, $id) 
    {
        $where = $this->getPrimaryKey() . " = '" . $id . "'";
        return $this->update($values, $where);
    }

    //     function __sleep() {
    //        $this->connection = null;
    //    return array(array_keys(get_class_vars(get_class($this))));
    //    }

    //    function __wakeup() {
    //    $this->connection = Adept_Db_Factory::getConnection($this->connectionString);
    //    }

    public function getTableName() 
    {
        return $this->tableName;
    }

    public function setTableName($tableName)
    {
        $this->tableName = $tableName;
        $this->tableInfo = $this->retriveTableInfo();
    }

}
